home *** CD-ROM | disk | FTP | other *** search
/ The CICA Windows Explosion! / The CICA Windows Explosion! - Disc 2.iso / programr / fileutil.zip / RM.C < prev    next >
C/C++ Source or Header  |  1992-02-22  |  17KB  |  672 lines

  1. /* `rm' file deletion utility for GNU.
  2.    Copyright (C) 1988, 1989, 1990 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 1, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Written by Paul Rubin, David MacKenzie, and Richard Stallman. */
  19.  
  20. /* MS-DOS port (c) 1990 by Thorsten Ohl, ohl@gnu.ai.mit.edu
  21.    This port is also distributed under the terms of the
  22.    GNU General Public License as published by the
  23.    Free Software Foundation.
  24.  
  25.    Please note that this file is not identical to the
  26.    original GNU release, you should have received this
  27.    code as patch to the official release.  */
  28.  
  29. #ifdef MSDOS
  30. static char RCS_Id[] =
  31.   "$Header: e:/gnu/fileutil/RCS/rm.c 1.4.0.3 90/09/20 08:46:14 tho Exp $";
  32.  
  33. static char Program_Id[] = "rm";
  34. static char RCS_Revision[] = "$Revision: 1.4.0.3 $";
  35.  
  36. #define VERSION \
  37.   "GNU %s, Version %.*s (compiled %s %s for MS-DOS)\n", Program_Id, \
  38.   (sizeof RCS_Revision - 14), (RCS_Revision + 11), __DATE__, __TIME__
  39.  
  40. #define COPYING \
  41.   "This is free software, distributed under the terms of the\n" \
  42.   "GNU General Public License.  For details, see the file COPYING.\n"
  43. #endif /* MSDOS */
  44.  
  45. #include <stdio.h>
  46. #include <getopt.h>
  47. #include <sys/types.h>
  48. #include <errno.h>
  49. #include "system.h"
  50.  
  51. #ifdef STDC_HEADERS
  52. #include <stdlib.h>
  53. #else
  54. char *malloc ();
  55. char *realloc ();
  56.  
  57. extern int errno;
  58. #endif
  59.  
  60. #ifdef MSDOS
  61. #include <string.h>
  62. #include <malloc.h>
  63. #include <io.h>
  64. #include <direct.h>
  65.  
  66. #include <gnulib.h>
  67.  
  68. extern  void main (int argc, char **argv);
  69. static  int rm (void);
  70. static  int remove_file (struct stat *statp);
  71. static  int remove_dir (struct stat *statp);
  72. static  int clear_directory (struct stat *statp);
  73. static  int yesno (void);
  74. static  char *basename (char *name);
  75. static  char *stpcpy (char *dest, char *source);
  76. static  void usage (void);
  77.  
  78. extern  int eaccess (char *path, int mode);
  79. extern  int eaccess_stat (struct stat *statp, int mode);
  80.  
  81. static  void strip_trailing_slashes (char **path);
  82. #define strip_trailing_slashes(path)    strip_trailing_slashes (&path)
  83. #define check_stack(stck, ino)        0
  84. #define unlink(name)            force_unlink (name)
  85. static int force_unlink (char *filename);
  86.  
  87. #else /* not MSDOS */
  88.  
  89. char *basename ();
  90. char *stpcpy ();
  91. char *xmalloc ();
  92. char *xrealloc ();
  93. int check_stack ();
  94. int clear_directory ();
  95. int eaccess_stat ();
  96. int remove_dir ();
  97. int remove_file ();
  98. int rm ();
  99. int yesno ();
  100. void error ();
  101. void strip_trailing_slashes ();
  102. void usage ();
  103.  
  104. #endif /* not MSDOS */
  105.  
  106.  
  107. /* Path of file now being processed; extended as necessary. */
  108. char *pathname;
  109.  
  110. /* Number of bytes currently allocated for `pathname';
  111.    made larger when necessary, but never smaller.  */
  112. int pnsize;
  113.  
  114. /* Name this program was run with.  */
  115. char *program_name;
  116.  
  117. /* If nonzero, display the name of each file removed. */
  118. int verbose;
  119.  
  120. /* If nonzero, ignore nonexistant files. */
  121. int ignore_missing_files;
  122.  
  123. /* If nonzero, recursively remove directories. */
  124. int recursive;
  125.  
  126. /* If nonzero, query the user about whether to remove each file. */
  127. int interactive;
  128.  
  129. /* If nonzero, remove directories with unlink instead of rmdir, and don't
  130.    require a directory to be empty before trying to unlink it.
  131.    Only works for the super-user. */
  132. int unlink_dirs;
  133.  
  134. /* Information for detecting attempted removal of `.' and `..'. */
  135. dev_t dot_dev, dotdot_dev;
  136. ino_t dot_ino, dotdot_ino;
  137.  
  138. struct option long_opts[] =
  139. {
  140. #ifdef MSDOS
  141.   {"copying", 0, NULL, 30},
  142.   {"version", 0, NULL, 31},
  143. #endif
  144.   {"directory", 0, &unlink_dirs, 1},
  145.   {"force", 0, NULL, 'f'},
  146.   {"interactive", 0, NULL, 'i'},
  147.   {"recursive", 0, &recursive, 1},
  148.   {"verbose", 0, &verbose, 1},
  149.   {NULL, 0, NULL, 0}
  150. };
  151.  
  152. void
  153. main (argc, argv)
  154.      int argc;
  155.      char **argv;
  156. {
  157.   int err = 0;
  158.   int c;
  159.   int ind;
  160.   struct stat stats;
  161.  
  162.   verbose = ignore_missing_files = recursive = interactive
  163.     = unlink_dirs = 0;
  164.   pnsize = 256;
  165.   program_name = argv[0];
  166.   pathname = xmalloc (pnsize);
  167.  
  168.   while ((c = getopt_long (argc, argv, "dfirvR", long_opts, &ind)) != EOF)
  169.     {
  170.       switch (c)
  171.     {
  172.     case 0:            /* Long option. */
  173.       break;
  174.     case 'd':
  175.       unlink_dirs = 1;
  176.       break;
  177.     case 'f':
  178.       ignore_missing_files = 1;
  179.       interactive = 0;
  180.       break;
  181.     case 'i':
  182.       ignore_missing_files = 0;
  183.       interactive = 1;
  184.       break;
  185.     case 'r':
  186.     case 'R':
  187.       recursive = 1;
  188.       break;
  189.     case 'v':
  190.       verbose = 1;
  191.       break;
  192. #ifdef MSDOS
  193.     case 30:
  194.       fprintf (stderr, COPYING);
  195.       exit (0);
  196.       break;
  197.     case 31:
  198.       fprintf (stderr, VERSION);
  199.       exit (0);
  200.       break;
  201. #endif
  202.     default:
  203.       usage ();
  204.     }
  205.     }
  206.  
  207.   if (optind == argc)
  208.     usage ();
  209.  
  210. #ifndef MSDOS            /* this fails at the root ... */
  211.   if (lstat (".", &stats))
  212.     error (1, errno, ".");
  213.   dot_dev = stats.st_dev;
  214.   dot_ino = stats.st_ino;
  215.   if (lstat ("..", &stats))
  216.     error (1, errno, "..");
  217.   dotdot_dev = stats.st_dev;
  218.   dotdot_ino = stats.st_ino;
  219. #endif /* not MSDOS */
  220.  
  221.   for (; optind < argc; optind++)
  222.     {
  223.       int len;
  224.  
  225.       strip_trailing_slashes (argv[optind]);
  226.       len = strlen (argv[optind]);
  227.       if (len + 1 > pnsize)
  228.     {
  229.       free (pathname);
  230.       pnsize = 2 * (len + 1);
  231.       pathname = xmalloc (pnsize);
  232.     }
  233.       strcpy (pathname, argv[optind]);
  234.       err += rm ();
  235.     }
  236.  
  237.   exit (err > 0);
  238. }
  239.  
  240. /* Remove file or directory `pathname' after checking appropriate things.
  241.    Return 0 if `pathname' is removed, 1 if not. */
  242.  
  243. int
  244. rm ()
  245. {
  246.   struct stat path_stats;
  247.  
  248.   if (lstat (pathname, &path_stats))
  249.     {
  250.       if (errno == ENOENT && ignore_missing_files)
  251.     return 0;
  252.       error (0, errno, "%s", pathname);
  253.       return 1;
  254.     }
  255.  
  256. #ifndef MSDOS
  257.   if ((path_stats.st_dev == dot_dev && path_stats.st_ino == dot_ino)
  258.       || (path_stats.st_dev == dotdot_dev && path_stats.st_ino == dotdot_ino))
  259.     {
  260.       error (0, 0, "%s: cannot remove current directory or parent", pathname);
  261.       return 1;
  262.     }
  263. #endif /* not MSDOS */
  264.  
  265.   if ((path_stats.st_mode & S_IFMT) == S_IFDIR && !unlink_dirs)
  266.     return remove_dir (&path_stats);
  267.   else
  268.     return remove_file (&path_stats);
  269. }
  270.  
  271. /* Query the user if appropriate, and if ok try to remove the
  272.    non-directory `pathname', which STATP contains info about.
  273.    Return 0 if `pathname' is removed, 1 if not. */
  274.  
  275. int
  276. remove_file (statp)
  277.      struct stat *statp;
  278. {
  279.   if (interactive)
  280.     {
  281.       fprintf (stderr, "%s: remove %s`%s'? ", program_name,
  282.            (statp->st_mode & S_IFMT) == S_IFDIR ? "directory " : "",
  283.            pathname);
  284.       if (!yesno ())
  285.     return 1;
  286.     }
  287.  
  288.   if (verbose)
  289.     printf ("  %s\n", pathname);
  290.  
  291.   if (unlink (pathname))
  292.     {
  293.       error (0, errno, "%s", pathname);
  294.       return 1;
  295.     }
  296.   return 0;
  297. }
  298.  
  299. /* If not in recursive mode, print an error message and return 1.
  300.    Otherwise, query the user if appropriate, then try to recursively
  301.    remove directory `pathname', which STATP contains info about.
  302.    Return 0 if `pathname' is removed, 1 if not. */
  303.  
  304. int
  305. remove_dir (statp)
  306.      struct stat *statp;
  307. {
  308.   int err;
  309.   int writable;
  310.  
  311.   if (!recursive)
  312.     {
  313.       error (0, 0, "%s: is a directory", pathname);
  314.       return 1;
  315.     }
  316.  
  317. #ifdef S_IFLNK
  318.   if ((statp->st_mode & S_IFMT) == S_IFLNK)
  319.     writable = 1;
  320.   else
  321. #endif
  322.     writable = eaccess_stat (statp, W_OK) == 0;
  323.  
  324.   if (!writable)
  325.     {
  326.       error (0, 0, "%s: no write permission for directory", pathname);
  327.       return 1;
  328.     }
  329.  
  330.   if (interactive)
  331.     {
  332.       fprintf (stderr, "%s: recursively descend directory `%s'? ",
  333.            program_name, pathname);
  334.       if (!yesno ())
  335.     return 1;
  336.     }
  337.  
  338.   if (verbose)
  339.     printf ("  %s\n", pathname);
  340.  
  341.   err = clear_directory (statp);
  342.   if (err == 0)
  343.     {
  344.       if (interactive)
  345.     {
  346.       fprintf (stderr, "%s: remove directory `%s'? ",
  347.            program_name, pathname);
  348.       if (!yesno ())
  349.         return 1;
  350.     }
  351.       err = rmdir (pathname) != 0;
  352.       if (err != 0)
  353.     error (0, errno, "%s", pathname);
  354.     }
  355.   return err;
  356. }
  357.  
  358. /* An element in a stack of pointers into `pathname'.
  359.    `pathp' points to where in `pathname' the terminating '\0' goes
  360.    for this level's directory name. */
  361. struct pathstack
  362. {
  363.   struct pathstack *next;
  364.   char *pathp;
  365.   ino_t inum;
  366. };
  367.  
  368. /* Linked list of pathnames of directories in progress in recursive rm.
  369.    The entries actually contain pointers into `pathname'.
  370.    `pathstack' is the current deepest level. */
  371. static struct pathstack *pathstack = NULL;
  372.  
  373. /* Read directory `pathname' and remove all of its entries,
  374.    avoiding use of chdir.
  375.    On entry, STATP points to the results of stat on `pathname'.
  376.    Return 0 for success, error count for failure.
  377.    Upon return, `pathname' will have the same contents as before,
  378.    but its address might be different; in that case, `pnsize' will
  379.    be larger, as well. */
  380.  
  381. int
  382. clear_directory (statp)
  383.      struct stat *statp;
  384. {
  385.   DIR *dirp;
  386.   struct direct *dp;
  387.   char *name_space;        /* Copy of directory's filenames. */
  388.   char *namep;            /* Current entry in `name_space'. */
  389.   unsigned name_size;        /* Bytes allocated for `name_space'. */
  390.   ino_t *inode_space;        /* Copy of directory's inodes. */
  391.   ino_t *inodep;        /* Current entry in `inode_space'. */
  392.   unsigned inode_size;        /* Bytes allocated for `inode_space'. */
  393.   int name_length;        /* Length of filename in `namep' plus '\0'. */
  394.   int pathname_length;        /* Length of `pathname'. */
  395.   int err = 0;            /* Return status. */
  396.   struct pathstack pathframe;    /* New top of stack. */
  397.   struct pathstack *pp;        /* Temporary. */
  398.  
  399.   errno = 0;
  400.   dirp = opendir (pathname);
  401.   if (dirp == NULL)
  402.     {
  403.       error (0, errno, "%s", pathname);
  404.       return 1;
  405.     }
  406.  
  407. #ifdef MSDOS                /* stat () it ourselves ... */
  408.   statp->st_size = 0L;
  409.   for (dp = readdir (dirp); dp != NULL; dp = readdir (dirp))
  410.     statp->st_size += strlen(dp->d_name) + 1;
  411.   seekdir(dirp, 0L);
  412. #endif /* MSDOS */
  413.  
  414.   name_size = statp->st_size;
  415.   name_space = (char *) xmalloc (name_size);
  416.   namep = name_space;
  417.  
  418. #ifndef MSDOS
  419.   inode_size = statp->st_size;
  420.   inode_space = (ino_t *) xmalloc (inode_size);
  421.   inodep = inode_space;
  422. #endif /* not MSDOS */
  423.  
  424.   while ((dp = readdir (dirp)) != NULL)
  425.     {
  426.       /* Skip "." and ".." (some NFS filesystems' directories lack them). */
  427.       if (dp->d_name[0] != '.'
  428.       || (dp->d_name[1] != '\0'
  429.           && (dp->d_name[1] != '.' || dp->d_name[2] != '\0')))
  430.     {
  431.       unsigned size_needed = (namep - name_space) + NLENGTH (dp) + 2;
  432.  
  433.       if (size_needed > name_size)
  434.         {
  435.           char *new_name_space;
  436.  
  437.           while (size_needed > name_size)
  438.         name_size += 1024;
  439.  
  440.           new_name_space = xrealloc (name_space, name_size);
  441.           namep += new_name_space - name_space;
  442.           name_space = new_name_space;
  443.         }
  444.       namep = stpcpy (namep, dp->d_name) + 1;
  445.  
  446. #ifndef MSDOS
  447.       if (inodep == inode_space + inode_size)
  448.         {
  449.           ino_t *new_inode_space;
  450.  
  451.           inode_size += 1024;
  452.           new_inode_space = (ino_t *) xrealloc (inode_space, inode_size);
  453.           inodep += new_inode_space - inode_space;
  454.           inode_space = new_inode_space;
  455.         }
  456.       *inodep++ = dp->d_ino;
  457. #endif /* not MSDOS */
  458.     }
  459.     }
  460.   *namep = '\0';
  461.   closedir (dirp);
  462.   
  463.   pathname_length = strlen (pathname);
  464.  
  465.   for (namep = name_space, inodep = inode_space; *namep != '\0';
  466.        namep += name_length, inodep++)
  467.     {
  468.       name_length = strlen (namep) + 1;
  469.  
  470.       /* Satisfy GNU requirement that filenames can be arbitrarily long. */
  471.       if (pathname_length + 1 + name_length > pnsize)
  472.     {
  473.       char *new_pathname;
  474.  
  475.       pnsize = (pathname_length + 1 + name_length) * 2;
  476.       new_pathname = xrealloc (pathname, pnsize);
  477.       /* Update the all the pointers in the stack to use the new area. */
  478.       for (pp = pathstack; pp != NULL; pp = pp->next)
  479.         pp->pathp += new_pathname - pathname;
  480.       pathname = new_pathname;
  481.     }
  482.  
  483.       /* Add a new frame to the top of the path stack. */
  484.       pathframe.pathp = pathname + pathname_length;
  485.       pathframe.inum = *inodep;
  486.       pathframe.next = pathstack;
  487.       pathstack = &pathframe;
  488.  
  489.       /* Append '/' and the filename to current pathname, take care of the
  490.      file (which could result in recursive calls), and take the filename
  491.      back off. */
  492.  
  493.       *pathstack->pathp = '/';
  494.       strcpy (pathstack->pathp + 1, namep);
  495.  
  496.       /* If the i-number has already appeared, there's an error. */
  497.       if (check_stack (pathstack->next, pathstack->inum) || rm ())
  498.     err++;
  499.  
  500.       *pathstack->pathp = '\0';
  501.       pathstack = pathstack->next;    /* Pop the stack. */
  502.     }
  503.   free (name_space);
  504. #ifndef MSDOS
  505.   free (inode_space);
  506. #endif /* not MSDOS */
  507.   return err;
  508. }
  509.  
  510. #ifndef MSDOS
  511. /* If STACK does not already have an entry with the same i-number as INUM,
  512.    return 0. Otherwise, ask the user whether to continue;
  513.    if yes, return 1, and if no, exit.
  514.    This assumes that no one tries to remove filesystem mount points;
  515.    doing so could cause duplication of i-numbers that would not indicate
  516.    a corrupted file system. */
  517.  
  518. int
  519. check_stack (stack, inum)
  520.      struct pathstack *stack;
  521.      ino_t inum;
  522. {
  523.   struct pathstack *p;
  524.  
  525.   for (p = stack; p != NULL; p = p->next)
  526.     {
  527.       if (p->inum == inum)
  528.     {
  529.       fprintf (stderr, "\
  530. %s: WARNING: Circular directory structure.\n\
  531. This almost certainly means that you have a corrupted file system.\n\
  532. NOTIFY YOUR SYSTEM MANAGER.\n\
  533. Cycle detected:\n\
  534. %s\n\
  535. is the same file as\n", program_name, pathname);
  536.       *p->pathp = '\0';    /* Truncate pathname. */
  537.       fprintf (stderr, "%s\n", pathname);
  538.       *p->pathp = '/';    /* Put it back. */
  539.       fprintf (stderr, "%s: continue? ", program_name);
  540.       if (!yesno ())
  541.         exit (1);
  542.       return 1;
  543.     }
  544.     }
  545.   return 0;
  546. }
  547. #endif /* !MSDOS */
  548.  
  549. /* Query the user for a line from the keyboard;
  550.    return 1 if yes, 0 otherwise. */
  551.  
  552. int
  553. yesno ()
  554. {
  555.   int c, c2;
  556.  
  557.   fflush (stderr);
  558.   c = getchar ();
  559.   if (c == '\n')
  560.     return 0;
  561.   while ((c2 = getchar ()) != '\n' && c2 != EOF)
  562.     ;
  563.  
  564.   return c == 'y' || c == 'Y';
  565. }
  566.  
  567. /* Remove trailing slashes from PATH; they cause some system calls to fail. */
  568.  
  569. #ifdef MSDOS
  570. #undef strip_trailing_slashes
  571.  
  572. void
  573. strip_trailing_slashes (char **path)
  574. {
  575.   char *new_path = _fullpath (NULL, *path, 0);
  576.   free (*path);
  577.   *path = msdos_format_filename (new_path);
  578. }
  579.  
  580. #else /* not MSDOS */
  581.  
  582. void
  583. strip_trailing_slashes (path)
  584.      char *path;
  585. {
  586.   int last;
  587.  
  588.   last = strlen (path) - 1;
  589.   while (last > 0 && path[last] == '/')
  590.     path[last--] = '\0';
  591. }
  592.  
  593. char *
  594. xmalloc (n)
  595.      unsigned n;
  596. {
  597.   char *p;
  598.  
  599.   p = malloc (n);
  600.   if (p == 0)
  601.     error (2, 0, "virtual memory exhausted");
  602.   return p;
  603. }
  604.  
  605. char *
  606. xrealloc (p, n)
  607.      char *p;
  608.      unsigned n;
  609. {
  610.   p = realloc (p, n);
  611.   if (p == 0)
  612.     error (2, 0, "virtual memory exhausted");
  613.   return p;
  614. }
  615.  
  616. #endif /* not MSDOS */
  617.  
  618. /* Return NAME with any leading path stripped off.  */
  619.  
  620. char *
  621. basename (name)
  622.      char *name;
  623. {
  624.   char *base;
  625.  
  626.   base = rindex (name, '/');
  627.   return base ? base + 1 : name;
  628. }
  629.  
  630. /* Copy SOURCE into DEST, stopping after copying the first '\0', and
  631.    return a pointer to the '\0' at the end of DEST;
  632.    in other words, return DEST + strlen (SOURCE). */
  633.  
  634. char *
  635. stpcpy (dest, source)
  636.      char *dest;
  637.      char *source;
  638. {
  639.   while ((*dest++ = *source++) != 0)
  640.     /* Do nothing. */ ;
  641.   return dest - 1;
  642. }
  643.  
  644. void
  645. usage ()
  646. {
  647. #ifdef MSDOS
  648.   fprintf (stderr, "\
  649. Usage: %s [-dfirvR] [+directory] [+force] [+interactive] [+recursive]\n\
  650.        [+verbose] [+copying] [+version] path...\n",
  651. #else /* not MSDOS */
  652.   fprintf (stderr, "\
  653. Usage: %s [-dfirvR] [+directory] [+force] [+interactive] [+recursive]\n\
  654.        [+verbose] path...\n",
  655. #endif /* not MSDOS */
  656.        program_name);
  657.   exit (1);
  658. }
  659.  
  660. #ifdef MSDOS
  661. int
  662. force_unlink(char *filename)
  663. {
  664.   if (access( filename, 2))            /* read only */
  665.     if (chmod( filename, S_IREAD|S_IWRITE))
  666.       error (0, errno, "can't force write permission for %s", filename);
  667.  
  668. #undef unlink                    /* nasty tricks ... */
  669.   return unlink (filename);
  670. }
  671. #endif /* MSDOS */
  672.